package com.azxc.rapid.auth.sso;

import cn.hutool.core.util.*;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.azxc.rapid.auth.utils.CheckSign;
import com.fujieid.jap.core.JapUser;
import com.fujieid.jap.core.JapUserService;
import com.fujieid.jap.core.cache.JapCache;
import com.fujieid.jap.core.config.AuthenticateConfig;
import com.fujieid.jap.core.config.JapConfig;
import com.fujieid.jap.core.context.JapAuthentication;
import com.fujieid.jap.core.exception.JapException;
import com.fujieid.jap.core.exception.JapOauth2Exception;
import com.fujieid.jap.core.result.JapErrorCode;
import com.fujieid.jap.core.result.JapResponse;
import com.fujieid.jap.core.strategy.AbstractJapStrategy;
import com.fujieid.jap.http.JapHttpRequest;
import com.fujieid.jap.http.JapHttpResponse;
import com.fujieid.jap.oauth2.*;
import com.fujieid.jap.oauth2.pkce.PkceHelper;
import com.fujieid.jap.oauth2.token.AccessToken;
import com.fujieid.jap.oauth2.token.AccessTokenHelper;
import com.xkcoding.json.util.Kv;
import com.xkcoding.json.util.StringUtil;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description : 类描述
 * @Author : linym
 * Created by user on 2022-01-11 10:26:20
 */
public class Oauth2StrategyNew extends AbstractJapStrategy {
    public Oauth2StrategyNew(JapUserService japUserService, JapConfig japConfig) {
        super(japUserService, japConfig);
    }

    public Oauth2StrategyNew(JapUserService japUserService, JapConfig japConfig, JapCache japCache) {
        super(japUserService, japConfig, japCache);
    }

    public JapResponse authenticate(AuthenticateConfig config, JapHttpRequest request, JapHttpResponse response) {
        try {
            Oauth2Util.checkOauthCallbackRequest(request.getParameter("error"), request.getParameter("error_description"), "Oauth2strategy request failed.");
        } catch (JapOauth2Exception var14) {
            return JapResponse.error(var14.getErrorCode(), var14.getErrorMessage());
        }

        JapUser sessionUser = this.checkSession(request, response);
        if (null != sessionUser) {
            return JapResponse.success(sessionUser);
        } else {
            try {
                this.checkAuthenticateConfig(config, OAuthConfig.class);
            } catch (JapException var13) {
                return JapResponse.error(var13.getErrorCode(), var13.getErrorMessage());
            }

            OAuthConfig authConfig = (OAuthConfig)config;

            try {
                Oauth2Util.checkOauthConfig(authConfig);
            } catch (JapOauth2Exception var12) {
                return JapResponse.error(var12.getErrorCode(), var12.getErrorMessage());
            }

            boolean isPasswordOrClientMode = authConfig.getGrantType() == Oauth2GrantType.PASSWORD || authConfig.getGrantType() == Oauth2GrantType.CLIENT_CREDENTIALS;
            if (!Oauth2Util.isCallback(request, authConfig) && !isPasswordOrClientMode) {
                String authorizationUrl = this.getAuthorizationUrl(authConfig);
                return JapResponse.success(authorizationUrl);
            } else {
                AccessToken accessToken = null;

                try {
                    //大小写有问题 覆盖
                    // accessToken = AccessTokenHelper.getToken(request, authConfig, new Object[0]);
                    if(authConfig.getResponseType() == Oauth2ResponseType.CODE){
                        accessToken =  getAccessTokenOfAuthorizationCodeMode(request, authConfig);
                    }else{
                        accessToken = AccessTokenHelper.getToken(request, authConfig, new Object[0]);
                    }

                } catch (JapOauth2Exception var11) {
                    return JapResponse.error(var11.getErrorCode(), var11.getErrorMessage());
                }

                JapUser japUser = null;

                try {
                    japUser = this.getUserInfo(authConfig, accessToken);
                } catch (JapOauth2Exception var10) {
                    return JapResponse.error(var10.getErrorCode(), var10.getErrorMessage());
                }

                return null == japUser ? JapResponse.error(JapErrorCode.UNABLE_SAVE_USERINFO) : this.loginSuccess(japUser, request, response);
            }
        }
    }

    public JapResponse refreshToken(AuthenticateConfig config, String refreshToken) {
        try {
            this.checkAuthenticateConfig(config, OAuthConfig.class);
        } catch (JapException var7) {
            return JapResponse.error(var7.getErrorCode(), var7.getErrorMessage());
        }

        OAuthConfig authConfig = (OAuthConfig)config;
        if (authConfig.getGrantType() != Oauth2GrantType.REFRESH_TOKEN) {
            return JapResponse.error(JapErrorCode.INVALID_GRANT_TYPE);
        } else {
            AccessToken accessToken = null;

            try {
                accessToken = AccessTokenHelper.getToken((JapHttpRequest)null, authConfig, new Object[]{refreshToken});
            } catch (JapOauth2Exception var6) {
                return JapResponse.error(var6.getErrorCode(), var6.getErrorMessage());
            }

            return JapResponse.success(accessToken);
        }
    }

    public JapResponse revokeToken(AuthenticateConfig config, String accessToken) {
        try {
            this.checkAuthenticateConfig(config, OAuthConfig.class);
        } catch (JapException var6) {
            return JapResponse.error(var6.getErrorCode(), var6.getErrorMessage());
        }

        OAuthConfig authConfig = (OAuthConfig)config;
        Map<String, String> params = new HashMap(6);
        params.put("access_token", accessToken);
        Kv tokenInfo = Oauth2Util.request(authConfig.getRevokeTokenEndpointMethodType(), authConfig.getRevokeTokenUrl(), params);
        Oauth2Util.checkOauthResponse(tokenInfo, "Oauth2Strategy failed to revoke access_token. " + accessToken);
        return JapResponse.success();
    }

    public JapResponse getUserInfo(AuthenticateConfig config, AccessToken accessToken) {
        try {
            this.checkAuthenticateConfig(config, OAuthConfig.class);
        } catch (JapException var6) {
            return JapResponse.error(var6.getErrorCode(), var6.getErrorMessage());
        }

        OAuthConfig authConfig = (OAuthConfig)config;

        try {
            return JapResponse.success(this.getUserInfo(authConfig, accessToken));
        } catch (JapOauth2Exception var5) {
            return JapResponse.error(var5.getErrorCode(), var5.getErrorMessage());
        }
    }

    private JapUser getUserInfo(OAuthConfig authConfig, AccessToken accessToken) throws JapOauth2Exception {
        if (null != accessToken && !StringUtil.isEmpty(accessToken.getAccessToken())) {
            Map<String, String> params = new HashMap();
            //放置对应参数
			Long timestamp=System.currentTimeMillis();
            params.put("access_token", accessToken.getAccessToken());
			params.put("timestamp", String.valueOf(timestamp));
			params.put("appId", "d0317f3288");
 			String sk="1614dfa7008241919e1bb7e76ea6f19f";
			String url="access_token="+accessToken.getAccessToken();
			url+="&timestamp="+timestamp;
			url+="&appId=d0317f3288";

			String sign= CheckSign.getSign(sk,url);
			params.put("signature", sign);

            Kv userInfo = Oauth2Util.request(authConfig.getUserInfoEndpointMethodType(), authConfig.getUserinfoUrl(), params);
            Oauth2Util.checkOauthResponse(userInfo, "Oauth2Strategy failed to get userInfo with accessToken.");
            JapUser japUser = this.japUserService.createAndGetOauth2User(authConfig.getPlatform(), userInfo, accessToken);
            return ObjectUtil.isNull(japUser) ? null : japUser;
        } else {
            throw new JapOauth2Exception("Oauth2Strategy failed to get userInfo with accessToken. AccessToken is empty.");
        }
    }

    private String getAuthorizationUrl(OAuthConfig authConfig) {
        String url = null;
        if (authConfig.getResponseType() == Oauth2ResponseType.CODE || authConfig.getResponseType() == Oauth2ResponseType.TOKEN) {
            url = this.generateAuthorizationCodeGrantUrl(authConfig);
        }

        return url;
    }

    private String generateAuthorizationCodeGrantUrl(OAuthConfig authConfig) {
        Map<String, Object> params = new HashMap(6);
        params.put("response_type", authConfig.getResponseType());
        params.put("client_id", authConfig.getClientId());
        if (StrUtil.isNotBlank(authConfig.getCallbackUrl())) {
            params.put("redirect_uri", authConfig.getCallbackUrl());
        }

        if (ArrayUtil.isNotEmpty(authConfig.getScopes())) {
            params.put("scope", String.join(" ", authConfig.getScopes()));
        }

        String state = authConfig.getState();
        if (StrUtil.isBlank(state)) {
            state = RandomUtil.randomString(6);
        }

        params.put("state", authConfig.getState());
        JapAuthentication.getContext().getCache().set("_jap:state:".concat(authConfig.getClientId()), state);
        if (Oauth2ResponseType.CODE == authConfig.getResponseType() && authConfig.isEnablePkce()) {
            params.putAll(PkceHelper.generatePkceParameters(authConfig));
        }

        //转化为小写
        if(params.containsKey("response_type")){
            params.put("response_type",params.get("response_type").toString().toLowerCase());
        }
        String query = URLUtil.buildQuery(params, StandardCharsets.UTF_8);
        if(authConfig.getAuthorizationUrl().contains("?")){
            return  authConfig.getAuthorizationUrl().concat("&").concat(query);
        }else{
            return  authConfig.getAuthorizationUrl().concat("?").concat(query);
        }

    }

    private static  AccessToken getTokenNew(){
        return null;
    }

    private static AccessToken getAccessTokenOfAuthorizationCodeMode(JapHttpRequest request, OAuthConfig oAuthConfig) throws JapOauth2Exception {
        String state = request.getParameter("state");
        Oauth2Util.checkState(state, oAuthConfig.getClientId(), oAuthConfig.isVerifyState());
        String code = request.getParameter("code");
        Map<String, String> params = new HashMap(6);
        params.put("grant_type", Oauth2GrantType.AUTHORIZATION_CODE.name().toLowerCase());
        params.put("code", code);
        params.put("client_id", oAuthConfig.getClientId());
        params.put("client_secret", oAuthConfig.getClientSecret());
        if (StrUtil.isNotBlank(oAuthConfig.getCallbackUrl())) {
            params.put("redirect_uri", oAuthConfig.getCallbackUrl());
        }

        if (ArrayUtil.isNotEmpty(oAuthConfig.getScopes())) {
            params.put("scope", String.join(" ", oAuthConfig.getScopes()));
        }

        if (Oauth2ResponseType.CODE == oAuthConfig.getResponseType() && oAuthConfig.isEnablePkce()) {
            params.put("code_verifier", PkceHelper.getCacheCodeVerifier(oAuthConfig.getClientId()));
        }

        HttpRequest request2 =null;
        if(oAuthConfig.getAccessTokenEndpointMethodType().equals(Oauth2EndpointMethodType.GET)){
            request2 =HttpUtil.createGet(oAuthConfig.getTokenUrl());
        }else{
            request2 =HttpUtil.createPost(oAuthConfig.getTokenUrl());
        }

        request2.header("Content-Type","application/x-www-form-urlencoded");
        request2.header("Tenant-Id","000000");
        request2.header("Authorization","Basic c2FiZXI6c2FiZXJfc2VjcmV0");
        request2.header("Content-Type","application/x-www-form-urlencoded");
        String body="grant_type=authorization_code&scope=all&code="+request.getParameter("code")+
                "&redirect_uri="+oAuthConfig.getCallbackUrl();
       /* request2.body(body);
        HttpResponse response2 = request2.execute();
        System.out.println(response2.body());
        if(response2.getStatus()!=200){
            throw new JapOauth2Exception("Cannot access url: " + oAuthConfig.getTokenUrl() + " , method: " + oAuthConfig.getAccessTokenEndpointMethodType() + " , params: " + params + " , error details: " + response2.body());
        }*/
        //Kv tokenInfo = JsonUtil.parseKv(response2.body());
         Kv tokenInfo =  Oauth2Util.request(oAuthConfig.getAccessTokenEndpointMethodType(), oAuthConfig.getTokenUrl(), params);
        Oauth2Util.checkOauthResponse(tokenInfo, "Oauth2Strategy failed to get AccessToken.");
        if (!tokenInfo.containsKey("access_token")) {
            throw new JapOauth2Exception("Oauth2Strategy failed to get AccessToken." + tokenInfo);
        } else {
            return mapToAccessToken(tokenInfo);
        }
    }

    private static AccessToken mapToAccessToken(Kv tokenMap) {
        return (new AccessToken()).setAccessToken(tokenMap.getString("access_token")).setRefreshToken(tokenMap.getString("refresh_token")).setIdToken(tokenMap.getString("id_token")).setTokenType(tokenMap.getString("token_type")).setScope(tokenMap.getString("scope")).setExpiresIn(tokenMap.getInteger("expires_in"));
    }
}
